package vethbr

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"

	"github.com/vishvananda/netlink"
)

// Network对象提供多个容器共用的网络环境，可以在环境内进行互相通信
// 其实就是网桥 bridge
type Network struct {
	Name      string
	IPRange   *net.IPNet // 地址段，其实就是 IP+Mask 也就是 172.20.0.0/24 这种
	Driver    string     // 网络驱动名，待会看看是啥！！
	GatewayIP net.IP     // 当前网络的网关，当删除的时候应该把该网关拥有的IP也一并释放
}

type NetworkList struct {
	list map[string]Network
}

// 从配置文件中读取所有的网络
func NewNetworkList() (*NetworkList, error) {
	if err := initNetLinkConfig(); err != nil {
		return nil, err
	}

	data, err := os.ReadFile(netLinkConfigPath)
	if err != nil {
		log.Println("[NewNetworkList]Error reading file:", netLinkConfigPath, err)
		return nil, err
	}

	nwl := &NetworkList{
		list: make(map[string]Network),
	}
	if len(data) == 0 {
		return nwl, nil
	}
	err = json.Unmarshal(data, &nwl.list)
	if err != nil {
		log.Println("[NewNetworkList]Error json.Unmarshal:", err)
		return nil, err
	}

	return nwl, nil
}

// 删除某个网络
// 注意：该方法未考虑多进程下的并发问题
func (nwl NetworkList) Remove(networkName string) error {
	delete(nwl.list, networkName)
	return nwl.dump()
}

// 添加新的网络链路
// 注意：该方法未考虑多进程下的并发问题
func (nwl *NetworkList) Add(nw *Network) error {
	// 以排他的方式读配置，并改写，避免其他进程改动改文件
	nwl.list[nw.Name] = *nw
	return nwl.dump()
}

func (nwl *NetworkList) List() map[string]Network {
	return nwl.list
}

func (nwl *NetworkList) Exist(name string) (Network, bool) {
	nw, exist := nwl.list[name]
	return nw, exist
}

func (nwl *NetworkList) dump() error {
	file, err := os.OpenFile(netLinkConfigPath, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	nwlJson, err := json.Marshal(nwl.list)
	if err != nil {
		return err
	}
	_, err = file.Write(nwlJson)
	return err
}

// Endpoint 用于连接 容器和网络。
// 保证容器内部与网络的通信。
// 其实就是 veth-pairs 和他的相关元信息
type Endpoint struct {
	ID          string           `json:"id"`
	Device      netlink.Veth     `json:"dev"`
	IPAddress   net.IP           `json:"ip"`
	MacAddress  net.HardwareAddr `json:"mac"` // 其实就是个字节数组 []byte
	Network     *Network         // 当前网络端点所处的网络环境
	PortMapping []string         // 当前网络端点提供的端口映射
}

// 基于提供的id生成端点数据
func NewEndpoint(id string) (*Endpoint, error) {
	// 创建一个新的 veth pairs
	vethPairMasterName := fmt.Sprintf("d0-%s", id)
	vethPairPeerName := fmt.Sprintf("d1-%s", id)
	vethPair := &netlink.Veth{
		// 偶数端除了MTU其余都默认属性
		LinkAttrs: netlink.LinkAttrs{
			Name: vethPairMasterName,
			MTU:  1500, // MTU（Maximum Transmission Unit），即最大传输单元。在早期的以太网规范（IEEE 802.3）里，就把 MTU 设定在了 1500 字节 。
		},
		// 奇数端
		PeerName: vethPairPeerName,
	}

	_, err := netlink.LinkByName(vethPairMasterName)
	if err != nil {
		log.Println("[在宿主机]不存在虚拟以太对:", vethPairMasterName, " , 新建一对")
		// 不存在，创建
		if err := netlink.LinkAdd(vethPair); err != nil {
			log.Printf("[在宿主机]无法创建veth-pair:%v\n", err)
			return nil, err
		}
	} else {
		log.Println("[在宿主机]已经存在虚拟以太对:", vethPairMasterName)
	}

	// 将虚拟网络接口的宿主机端设置为启动状态；容器端等到移动到容器内之后再设置为启动状态
	linkPeer, err := netlink.LinkByName(vethPairPeerName)
	if err != nil {
		log.Println("[在宿主机]获取宿主机端设备异常:", vethPairMasterName)
		return nil, err
	}
	err = netlink.LinkSetUp(linkPeer)
	if err != nil {
		log.Println("[在宿主机]开启宿主机端失败:", vethPairMasterName)
		return nil, err
	}

	return &Endpoint{
		ID:         id,
		Device:     *vethPair,
		MacAddress: generateMacAddress(),
	}, nil
}

// 定义一个网络驱动接口
// 所有实现该接口的对象应该能够 创建网络、连接端点到网络之中、断开端点和网络的连接
type Driver interface {
	Name() string
	Create(subnet string, name string) (*Network, error)   // 创建的时候可以声明网段范围
	Delete(name string)                                    // 删除这个网络
	Connect(network *Network, endpoint *Endpoint) error    // 连接端点到网络之中
	Disconnect(network *Network, endpoint *Endpoint) error // 断开端点和网络的连接
}

// 定义一个IP分配接口
type IPManager interface {
	// 从指定的ip地址段中分配一个可用的ip地址
	Allocate(subnet *net.IPNet) (ip net.IP, err error) // net.IPNet前面已经说明，其实就是 IP+Mask
	Release(subnet *net.IPNet, ipaddr net.IP) error    // 从指定的段中释放掉指定的IP
}
